// ===================================
// <copyright>
// This file is part of LABSMFController.
//
//    LABSMFController is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    LABSMFController is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with LABSMFController.  If not, see <http://www.gnu.org/licenses/>.
//    Copyright 2011 David Bakker
//
//</copyright>
// <author>David Bakker</author>
// <email>mailto:DavidFBakker@gmail.com</email>
// <created>Saturday, May 07, 2011</created>
// <lastedit>Saturday, May 07, 2011</lastedit>
// ===================================

using System;
using System.Threading;
using GHIElectronics.NETMF.Hardware;
using LABSMFController.LABS.Classses;
using LABSMFController.LABS.Devices;
using Microsoft.SPOT;

namespace LABSMFController.LABS.Services
{
    public class PWMServiceClass : BeerService
    {
        private bool _isAutoConfiguring;
        private object _locker;
        private PWMValveDevice[] _pwmValveDevices;
        private int[] _requestAnswers;

        public int MAXPWMalves
        {
            get { return 1; }
        }

        private object Locker
        {
            get { return _locker ?? (_locker = new object()); }
        }

        public PWMValveDevice[] PWMDevices
        {
            get
            {
                lock (Locker)
                {
                    if (_pwmValveDevices == null)

                        SetupVValves();

                    return _pwmValveDevices;
                }
            }
            set { _pwmValveDevices = value; }
        }

        public override void Start()
        {
            _requestAnswers = new int[MAXPWMalves];
            BeerProgram.Log("PWMService starting");
            SetupVValves();
        }

        public override void Stop()
        {
            BeerProgram.Log("PWMService stoping");
        }

        private bool GetAllAnswers()
        {
            bool alldone = true;

            for (int c = 0; c < MAXPWMalves; c++)
            {
                if (BeerProgram.QueueService.GetAnswer(_requestAnswers[c]) == null)
                {
                    alldone = false;
                }
            }
            return alldone;
        }

        // Move to 0, capture volts
        // Move to 90, capture volts
        // Move back to 0 and time it

        public override void AutoConfigure()
        {
            BeerProgram.Log("PWMService AutoConfiguriong");
            _isAutoConfiguring = true;

            //Set everything to 0
            for (int c = 0; c < MAXPWMalves; c++)
            {
                var op = new OperatePWMThread();
                op.DoRun(PWMDevices[c], 0);
                _requestAnswers[c] = BeerProgram.QueueService.AddToQ("PWM", "Respond when PWM valve " + c + " reaches 0",
                                                                     QueueResponseType.String);
            }
            bool alldone = false;
            while (!alldone)
            {
                alldone = true;
                for (int c = 0; c < MAXPWMalves; c++)
                {                   
                    if (BeerProgram.QueueService.GetAnswer(_requestAnswers[c]) == null)
                    {
                        alldone = false;
                    }
                    else
                    {
                        double v0 = PWMDevices[c].GetVolts();
                    }
                }
            }

            //Set everything to 90
            for (int c = 0; c < MAXPWMalves; c++)
            {
                var op = new OperatePWMThread();
                op.DoRun(PWMDevices[c], 90);
                _requestAnswers[c] = BeerProgram.QueueService.AddToQ("PWM",
                                                                     "Respond when PWM valve " + c + " reaches 90",
                                                                     QueueResponseType.String);
            }
         
            alldone = false;
            while (!alldone)
            {
                alldone = true;
                for (int c = 0; c < MAXPWMalves; c++)
                {
                  
                   
                    if (BeerProgram.QueueService.GetAnswer(_requestAnswers[c]) == null)
                    {
                        alldone = false;
                    }
                    else
                    {
                        double v0 = PWMDevices[c].GetVolts();
                    }
                }
            }

            long[] startTicks = new long[MAXPWMalves];
            
            // Set everything back to 0 and time it
            for (int c = 0; c < MAXPWMalves; c++)
            {
                var op = new OperatePWMThread();
                op.DoRun(PWMDevices[c], 0);
                _requestAnswers[c] = BeerProgram.QueueService.AddToQ("PWM", "Respond when PWM valve " + c + " reaches 0",
                                                                     QueueResponseType.String);
            }
             alldone = false;
            while (!alldone)
            {
                alldone = true;
                for (int c = 0; c < MAXPWMalves; c++)
                {
                    startTicks[c] = DateTime.Now.Ticks;
                    if (BeerProgram.QueueService.GetAnswer(_requestAnswers[c]) == null)
                    {
                        alldone = false;
                    }
                    else
                    {                        
                        var dt = new TimeSpan(DateTime.Now.Ticks - startTicks[c]);
                    }
                }
            }
        }

        public override bool CanAutoConfigureDone()
        {
            return true;
        }


        public void ResetAllPWMDevices()
        {
            var operatePWMThreads = new OperatePWMThread[MAXPWMalves];

            for (int c = 0; c < MAXPWMalves; c++)
            {
                operatePWMThreads[c] = new OperatePWMThread();
                operatePWMThreads[c].DoRun(PWMDevices[c], 0);
            }
        }

        private void SetupVValves()
        {
            Location.Setup();
            PWMDevices = new PWMValveDevice[MAXPWMalves];

            PWMDevices[0] = new PWMValveDevice("PWM Device for pump 0", Devs.DigitalDevice.Pump0_PWMValve.PIN,
                                               Devs.AnalogInDevice.Pump0_PWMValve.PIN, Devs.Settings.PWM0.VoltageStep,
                                               Devs.Settings.PWM0.Voltage0Angle);
            //PWMDevices[1] = new PWMValveDevice("PWM Device for pump 1", Devs.DigitalDevice.Pump1_PWMValve.PIN,
            //                                   Devs.AnalogInDevice.Pump1_PWMValve.PIN);
        }


        public string GetPWMClientOutPut()
        {
            string ret = "HEADER:PWMDevice:" + PWMDevices[0].Header + ";";
            for (int c = 0; c < MAXPWMalves; c++)
            {
                ret = ret + "\r\n" + PWMDevices[c].GetClientOutPut();
            }
            return ret;
        }

        public void SetPWMValveAngle(int valveNum, int angle)
        {
            var op = new OperatePWMThread();
            op.DoRun(PWMDevices[valveNum], angle);
        }
    }

    internal class OperatePWMThread
    {
        #region Delegates

        public delegate void TriggerHandler(object sender, string message);

        #endregion

        public PWMValveDevice PWMValveDev;
        private int _delayMilli;
        public double Angle { get; set; }
        public event TriggerHandler AngleReached;

        public void InvokeTriggerEvent(string message)
        {
            PWMValveDev.IsRunning = false;
            TriggerHandler handler = AngleReached;
            if (handler != null) handler(this, message);
        }

        public void DoRun(PWMValveDevice pwmValveDevice, double angle)
        {
            if (angle < 0 || angle > 90)
            {
                return;
            }
            Runner(pwmValveDevice, angle);
        }

        private void Runner(PWMValveDevice pwmValveDevice, double angle)
        {
            PWMValveDev = pwmValveDevice;
            pwmValveDevice.IsRunning = true;
            _delayMilli = 500;
            Angle = angle;
            var thrd = new Thread(DoRun);

            thrd.Start();
        }


        private void DoRun()
        {
            int startAngle = PWMValveDev.GetAngle();
            uint[] timings = PWMValveDev.GetTimings(Angle);

            PWMValveDev.DigitalOutPutCompare.Set(true, timings, 0, 2, true);

            while (!PWMValveDev.CheckAngle(startAngle, Angle))
            {
                Debug.Print("Current angle " + PWMValveDev.CurrentAngle);
                Thread.Sleep(_delayMilli);
            }

            if (Angle == 0)
            {
                PWMValveDev.DigitalOutPutCompare.Dispose();
                PWMValveDev.DigitalOutPutCompare = new OutputCompare(PWMValveDev.DigitalPulsePin, false, 2);
            }
            InvokeTriggerEvent("Angle " + Angle + " reached");
        }
    }
}